/* 
Scenic Graphics Library
Copyright (C) 2007 Jouni Tulkki

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USApackage scenic;
*/

package scenic;

import java.awt.Rectangle;
import java.awt.geom.AffineTransform;

import scenic.path.Path;
import scenic.path.PathWalker;

/**
 * This shape strokes a path using the given parameters.
 */
public class StrokedPath extends SceneShape {
	private Path path;
	private double lineWidth = 1.0;
	private int endCap = LineCapStyle.BUTT_CAP;
	private int lineJoin = LineJoinStyle.BEVEL_JOIN;
	private double miterLimit = 10.0;
	private float[] dashArray;
	private double dashPhase;
	
	private class Walker implements PathWalker
	{
		private DrawContext context;
		
		public Walker(DrawContext context) {
			this.context = context;
		}
		
		public void beginSubPath(boolean isClosed) {
			context.renderer.polylineBegin(context.context, isClosed);			
		}

		public void endSubPath() {
			context.renderer.polylineEnd(context.context);			
		}
	
		public void lineTo(double x, double y) {
			context.renderer.polylinePoint(context.context, x, y);			
		}
	}
	
	/**
	 * Creates a StrokedPath object with the given path.
	 * 
	 * @param path the path to be stroked.
	 */
	public StrokedPath(Path path) {
		this.path = path;
	}
	
	/**
	 * Gets the path to be stroked.
	 */
	public Path getPath() {
		return path;
	}
	
	/**
	 * Sets the path to be stroked.
	 */
	public void setPath(Path path) {
		this.path = path;
		changed();
	}
	
	/**
	 * Gets the line width. The line width is defined in logical units.
	 */
	public double getLineWidth() {
		return lineWidth;
	}

	/**
	 * Sets the line width. The line width is defined in logical units.
	 */
	public void setLineWidth(double lineWidth) {
		this.lineWidth = lineWidth;
		changed();
	}

	/**
	 * Gets the line cap style.
	 */
	public int getEndCap() {
		return endCap;
	}

	/**
	 * Sets the line cap style. The line cap style must be one of the 
	 * constants in the LineCapStyle interface.
	 */
	public void setEndCap(int lineCap) {
		this.endCap = lineCap;
		changed();
	}

	/**
	 * Gets the line join style.
	 */
	public int getLineJoin() {
		return lineJoin;
	}

	/**
	 * Sets the line join style. The line join style must be one of the
	 * constants in the LineJoinStyle interface.
	 */
	public void setLineJoin(int lineJoin) {
		this.lineJoin = lineJoin;
		changed();
	}
	
	/**
	 * Gets the miter limit.
	 */
	public double getMiterLimit() {
		return miterLimit;
	}

	/**
	 * Sets the miter limit. The miter limit defines the maximum length
	 * of the spike when using miter joins. If the spike is longer then the
	 * miter limit a bevel join is used instead.
	 */
	public void setMiterLimit(double limit) {
		miterLimit = limit;
		changed();
	}
		
	/**
	 * Gets the line dash lengths.
	 */
	public float[] getDashArray() {
		return dashArray;
	}

	/**
	 * Sets the line dash pattern. The dash pattern is defined
	 * using an array which contains the lengths of consecutive
	 * visible and non-visible portions of the dash pattern. The 
	 * lenths are given in logical units. 
	 * 
	 * @param lineDashLengths the line dash pattern.
	 */
	public void setDashArray(float[] lineDashLengths) {
		this.dashArray = lineDashLengths;
		changed();
	}

	/**
	 * Gets the line dash phase.
	 */
	public double getDashPhase() {
		return dashPhase;
	}

	/**
	 * Sets the phase of the line dash pattern. The phase defines the
	 * starting position of the line dash pattern.
	 * 
	 * @param lineDashPhase the line dash phase.
	 */
	public void setDashPhase(double lineDashPhase) {
		this.dashPhase = lineDashPhase;
		changed();
	}

	void draw(DrawContext context, AffineTransform transform, Color color) {
		context.renderer.color(context.context, color);
		context.renderer.setTransform(context.context, transform);
		context.renderer.polylineSetStyle(context.context, (float)lineWidth, 
				endCap, lineJoin, (float)miterLimit,
				dashArray, dashPhase);
		path.walk(new Walker(context), transform, context.pathError);
	}
	
	int getDrawType(DrawContext context, AffineTransform transform, Color color) {
		if(color.alpha != 1.0f)
			return SceneShape.DRAW_SURFACE;
		return SceneShape.DRAW_SIMPLE;
	}
	
	Rectangle getBounds(DrawContext context, AffineTransform transform) {
		Rectangle r = path.getBounds(transform);
		Rectangle wr = Util.transform(transform, new Rectangle(0, 0, 
				(int)Math.ceil(lineWidth / 2.0 * 4.0),
				(int)Math.ceil(lineWidth / 2.0 * 4.0)));
		
		r.x -= wr.width;
		r.y -= wr.width;
		r.width += wr.width * 2;
		r.height += wr.height * 2;
		
		return r;
	}	
}
